package org.vaadin.passwordfield;

import com.vaadin.data.Property;
import com.vaadin.terminal.ClassResource;
import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.PaintTarget;
import com.vaadin.ui.TextField;

/**
 * A specialized field for password entry.
 * <p>
 * Displays a warning icon if CapsLock is on, with a tooltip message if hovering
 * the icon. Both icon and message can be customized, and the functionality can
 * be turned off altogether.
 * </p>
 * <p>
 * Displays a "chroma-hash" colored 'flag' generated from the password. The same
 * password will always generate the same flag, thus indicating to the user
 * whether or not the password was correctly input. <br/>
 * Note that knowing the flag does not help in guessing the password - it's a
 * hash of the password, and one that generates particularly many collisions,
 * which is perfect for this. <br/>
 * The exact same flag can also be generated on the server, for example to be
 * displayed to the user in his/hers account info along with information about
 * how to use the chroma-hash flag. <br/>
 * The functionality can also be disabled altogether.
 * </p>
 * <p>
 * By default both CapsLock warning and chroma-hash flag will be enabled. <code>
 * </code>
 * </p>
 * 
 */
@com.vaadin.ui.ClientWidget(org.vaadin.passwordfield.client.ui.VPasswordField.class)
public class PasswordField extends TextField {

    private static final long serialVersionUID = -1349631485917911679L;

    private String capsLockWarning = "CapsLock seems to be on";
    private ClassResource capsLockIcon;
    private String flagDescription = "If you type your password correctly, it will always generate the same 'flag'";

    public PasswordField() {
        super();
        setSecret(true);
    }

    public PasswordField(Property dataSource) {
        super(dataSource);
        setSecret(true);
    }

    public PasswordField(String caption, Property dataSource) {
        super(caption, dataSource);
        setSecret(true);
    }

    public PasswordField(String caption, String value) {
        super(caption, value);
        setSecret(true);
    }

    public PasswordField(String caption) {
        super(caption);
        setSecret(true);
    }

    /**
     * Gets the message shown when the user hovers the warning icon.
     * 
     * @return the current message
     */
    public String getCapsLockWarning() {
        return capsLockWarning;
    }

    /**
     * Sets the message shown when the user hovers the capslock warning icon.
     * Setting it to null will disable the CapsLock warning.
     * 
     * @param capsLockWarning
     *            the message to show when the user hovers the warning icon, or
     *            null to disable
     */
    public void setCapsLockWarning(String capsLockWarning) {
        this.capsLockWarning = capsLockWarning;
        requestRepaint();
    }

    /**
     * Gets the resource currently used as capslock warning icon.
     * 
     * @return the current warning icon
     */
    public ClassResource getCapsLockWarningIcon() {
        return capsLockIcon;
    }

    /**
     * Sets the resource used as capslock warning icon.
     * 
     * @param capsLockIcon
     *            the resource to use as warning icon
     */
    public void setCapsLockWarningIcon(ClassResource capsLockIcon) {
        this.capsLockIcon = capsLockIcon;
        requestRepaint();
    }

    /**
     * Gets the description text shown (as tooltip) when the user hovers the
     * chroma-hash 'flag'.
     * 
     * @return the current 'flag' description
     */
    public String getFlagDescription() {
        return flagDescription;
    }

    /**
     * Sets the description text shown (as tooltip) when the user hovers the
     * chroma-hash 'flag'. Setting the description to null will disable the
     * chroma-hash flag.
     * 
     * @param flagDescription
     *            the desired 'flag' description or null to disable
     */
    public void setFlagDescription(String flagDescription) {
        this.flagDescription = flagDescription;
    }

    @Override
    public void paintContent(PaintTarget target) throws PaintException {
        super.paintContent(target);

        if (capsLockWarning != null) {
            target.addAttribute("capswarning", capsLockWarning);
        }

        if (capsLockIcon == null) {
            capsLockIcon = new ClassResource("capslock.png", getApplication());
        }
        target.addAttribute("clwicon", capsLockIcon);

        if (flagDescription != null) {
            target.addAttribute("flagdesc", flagDescription);
        }
    }

    /**
     * This field is always secret, throws if one attempts to unset the secret
     * state.
     * 
     * @see com.vaadin.ui.TextField#setSecret(boolean)
     */
    @Override
    public void setSecret(boolean secret) {
        if (!secret) {
            throw new UnsupportedOperationException(getClass().getSimpleName()
                    + " is always secret.");
        }
        super.setSecret(secret);
    }

}
